package com.qunduikj.sjb.sdk.media;

import android.content.Context;
import android.os.Environment;
import android.os.Handler;
import com.litesuits.android.log.Log;
import com.litesuits.common.io.FileUtils;
import com.qunduikj.sjb.sdk.AppSdk;

import java.io.File;
import java.io.IOException;
import java.util.UUID;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

/**
 *
 * 录制语音
 *
 * Created by wangj on 15/8/8.
 */
public class VoiceRecorder {

    private static final String TAG = VoiceRecorder.class.getSimpleName();

    private final Context context;
    private android.media.MediaRecorder mediaRecorder;
    private volatile String id;

    public static final String SDCARD = Environment.getExternalStorageDirectory().getPath();

    public static final String VOICE_PATH = SDCARD + "/SiJiBang/" + "tmp/voice/";// 录制语音、图片保存目录

    // private volatile String ext;
    private volatile long lastStartTimeMillis;
    private static final int MIN_RECORD_TIME_MILLIS = 1000;
    private static final int MAX_RECORD_TIME_MILLIS = 60000;

    private Runnable minTimeReachThread;

    private volatile OnRecordStopedListener onRecordStopedListener;

    private final ExecutorService singleThreadPool;

    private final Handler handler = new Handler();

    private boolean isStarted = false;

    Runnable mUpdateMicStatusTimer;
    Runnable mCountdownRunner;

    class UpdateMicStatusTimer implements Runnable {
        @Override
        public void run() {
            updateMicStatus();
        }

    }

    class CountdownRunnable implements Runnable {
        int count = 0;

        @Override
        public void run() {
            count++;
            if (count > 50) {
                final int countdown = Math.max(60 - count, 0);
                notifyRecordCountdown(countdown);
            }
            handler.postDelayed(mCountdownRunner, 1000);
        }
    }

    private void notifyRecordCountdown(final int countdown) {
        handler.post(new Runnable() {
            @Override
            public void run() {
                if (onRecordStopedListener != null) {
                    onRecordStopedListener.onCountDown(countdown);

                }
            }
        });
    }

    public VoiceRecorder(Context context) {
        this.context = context;
        singleThreadPool = Executors.newSingleThreadExecutor();
        checkFile();
    }


    private void stopMediaRecorder() {
        Log.d("stop media recorder");
        try {
            isStarted = false;
            mediaRecorder.stop();
            mediaRecorder.reset(); // You can reuse the object by going back to setAudioSource() step
            mediaRecorder.release();
        } catch (Exception e) {
        }
    }


    private void reset() {
        stopMediaRecorder();
        FileUtils.deleteQuietly(new File(VOICE_PATH + File.separator + id));
    }


    private void notifyRecordStop(long time) {
        byte[] bs = null;
        try {
            bs = FileUtils.readFileToByteArray(new File(VOICE_PATH + File.separator
                    + id));
            onRecordStopedListener.onRecordStoped(id, bs, time);
        } catch (IOException e) {
            Log.d(TAG, e);
            onRecordStopedListener.onRecordFailed();
        }
    }

    private void notifyRecordContentTooShort() {

        handler.post(new Runnable() {
            @Override
            public void run() {
                if (onRecordStopedListener != null) {
                    onRecordStopedListener.onTooShort();
                }
            }
        });

    }

    private void notifyRecordFailed() {
        if (onRecordStopedListener != null) {
            onRecordStopedListener.onRecordFailed();
        }
    }

    private String createTempRecordFilePath(final String id) {
        return VOICE_PATH + File.separator + id;
    }

    private android.media.MediaRecorder.OnErrorListener createMediaRecorderErrorListener() {
        return new android.media.MediaRecorder.OnErrorListener() {
            @Override
            public void onError(android.media.MediaRecorder mr, int what, int extra) {
                Log.d("error:" + what + "," + extra);
            }
        };
    }

    private android.media.MediaRecorder.OnInfoListener createMediaRecorderInfoListener() {
        return new android.media.MediaRecorder.OnInfoListener() {
            @Override
            public void onInfo(android.media.MediaRecorder mr, int what, int extra) {
                Log.d("info:" + what + "," + extra);
                if (what == android.media.MediaRecorder.MEDIA_RECORDER_INFO_MAX_DURATION_REACHED) {

                    removeUICallbackUpdateMicStatus();

                    stopMediaRecorder();
                    notifyRecordStop(MAX_RECORD_TIME_MILLIS);
                }
            }
        };
    }

    private android.media.MediaRecorder createMediaRecorder(final OsType osType) {
        final android.media.MediaRecorder mediaRecorder = new android.media.MediaRecorder();
        mediaRecorder.setOnInfoListener(createMediaRecorderInfoListener());
        mediaRecorder.setOnErrorListener(createMediaRecorderErrorListener());
        mediaRecorder.setAudioSource(android.media.MediaRecorder.AudioSource.MIC);
        mediaRecorder.setOutputFormat(android.media.MediaRecorder.OutputFormat.RAW_AMR);// RAW_AMR
        mediaRecorder.setAudioEncoder(osType == OsType.IPHONE ? android.media.MediaRecorder.AudioEncoder.AMR_NB
                : android.media.MediaRecorder.AudioEncoder.AMR_NB);//  以前iOS是这个格式： MediaRecorder.AudioEncoder.AAC
        id = UUID.randomUUID().toString();
        mediaRecorder.setOutputFile(createTempRecordFilePath(id));
        mediaRecorder.setMaxDuration(MAX_RECORD_TIME_MILLIS);// 最长录制60S
        try {
            mediaRecorder.prepare();
        } catch (Exception e) {
            Log.d(TAG, e);
        }
        return mediaRecorder;
    }

    private void notifyRecordStart(final OnRecordStartedListener onRecordStartedListener) {
        handler.post(new Runnable() {
            @Override
            public void run() {
                onRecordStartedListener.onRecordStarted();
            }
        });
    }

    private void notifyRecordReachMinTime(final OnRecordStartedListener onRecordStartedListener) {
        minTimeReachThread = new Runnable() {
            @Override
            public void run() {
                onRecordStartedListener.onReachMinTime();
            }
        };

        handler.postDelayed(minTimeReachThread, MIN_RECORD_TIME_MILLIS);
    }


    private void initUIHandler(final OnRecordStartedListener onRecordStartedListener) {

        notifyRecordStart(onRecordStartedListener);
        notifyRecordReachMinTime(onRecordStartedListener);

        mUpdateMicStatusTimer = new UpdateMicStatusTimer();
        handler.post(mUpdateMicStatusTimer);//获取音量

        mCountdownRunner = new CountdownRunnable();
        handler.post(mCountdownRunner);
    }

    /**
     * 开始录制
     *
     * @throws IOException
     * @throws IllegalStateException
     */
    public void startRecord(final OnRecordStartedListener onRecordStartedListener,
                            final OnRecordStopedListener onRecordStopedListener, final OsType osType) {


        singleThreadPool.submit(new Runnable() {

            @Override
            public void run() {

                VoiceRecorder.this.onRecordStopedListener = onRecordStopedListener;
                if (isStarted) {
                    reset();
                }
                try {

                    isStarted = true;
                    mediaRecorder = createMediaRecorder(osType);
                    mediaRecorder.start(); // Recording is now started

                    initUIHandler(onRecordStartedListener);

                    lastStartTimeMillis = AppSdk.getInstance().getCurrentTimeMillis();

                } catch (Exception e) {
                    Log.d(TAG, e + " ------------------------------------");
                }
            }

        });

    }

    /**
     * 更新话筒状态 分贝是也就是相对响度 分贝的计算公式K=20lg(Vo/Vi) Vo当前振幅值 Vi基准值为600：我是怎么制定基准值的呢？ 当20 *
     * Math.log10(mMediaRecorder.getMaxAmplitude() / Vi)==0的时候vi就是我所需要的基准值
     * 当我不对着麦克风说任何话的时候，测试获得的mMediaRecorder.getMaxAmplitude()值即为基准值。 Log.i("mic_", "麦克风的基准值：" +
     * mMediaRecorder.getMaxAmplitude());前提时不对麦克风说任何话
     */
    private int BASE = 1000;
    private int SPACE = 300;// 间隔取样时间

    private void updateMicStatus() {
        if (mediaRecorder != null && onRecordStopedListener != null) {
            // int vuSize = 10 * mMediaRecorder.getMaxAmplitude() / 32768;
            int ratio = mediaRecorder.getMaxAmplitude() / BASE;
            int db = 0;// 分贝
            if (ratio > 1) {
                db = (int) (20 * Math.log10(ratio));
            }
            Log.d(TAG, "---------- 当前分呗率：" + db);
            onRecordStopedListener.onUpdateMicLevel(db / 4);

            handler.postDelayed(mUpdateMicStatusTimer, SPACE);
        }
    }


    private void removeUICallbackReachMinTime() {
        if (minTimeReachThread != null) {
            handler.removeCallbacks(minTimeReachThread);
        }
    }

    private void removeUICallbackUpdateMicStatus() {
        if (mUpdateMicStatusTimer != null) {
            handler.removeCallbacks(mUpdateMicStatusTimer);
        }
    }

    private void removeUICallbackCountdown() {
        if (mCountdownRunner != null) {
            handler.removeCallbacks(mCountdownRunner);
        }
    }


    /**
     * 结束录制
     */
    public void stopRecord() {
        singleThreadPool.submit(new Runnable() {
            @Override
            public void run() {

                removeUICallbackReachMinTime();
                removeUICallbackUpdateMicStatus();
                removeUICallbackCountdown();

                if (!isStarted) {
                    notifyRecordFailed();
                    return;
                }

                try {
                    final long deltaTime = AppSdk.getInstance().getCurrentTimeMillis() - lastStartTimeMillis;

                    if (deltaTime < MIN_RECORD_TIME_MILLIS) {
                        notifyRecordContentTooShort();
                        reset();
                        return;
                    }

                    stopMediaRecorder();
                    handler.post(new Runnable() {
                        @Override
                        public void run() {
                            notifyRecordStop(deltaTime);
                        }
                    });

                } catch (Exception e) {
                    Log.d("", e);
                } finally {
                    isStarted = false;
                }
            }
        });
    }


    /**
     * 释放资源
     */
    public void destroy() {
        if (mediaRecorder != null) {
            // mediaRecorder.release();
        }
        singleThreadPool.shutdown();
    }

    private void checkFile() {
        File _file = new File(VOICE_PATH);
        if (!_file.exists()) {
            _file.mkdirs();
        }
    }

    public static void checkFile(String path) {
        File file = new File(path);
        if (!file.exists()) {
            file.mkdir();
        }
    }

    /**
     *  删除文件下的所有文件
     *
     * @param filePath
     */
    public void deleteAllFileByPath(String filePath) {
        try {
            File file = new File(filePath);

            if(file.exists() && file.isDirectory()) {
                File[] childFile = file.listFiles();
                if(childFile != null && childFile.length != 0) {
                    file.delete();
                }
            }
        } catch (Exception e) {
            Log.e(TAG, e.toString());
        }
    }

    public static interface OnRecordStopedListener {
        void onRecordStoped(String fileId, byte[] voiceBytes, long duration);

        void onRecordFailed();

        void onUpdateMicLevel(int level);

        /**
         * 录音时间太短
         */
        void onTooShort();

        void onCountDown(int countDown);
    }

    public static interface OnRecordStartedListener {
        void onRecordStarted();

        /**
         * 到达最短录音时间
         */
        void onReachMinTime();
    }


    public enum OsType {
        ANDROID(0),
        IPHONE(1),
        WP8(2),
        PC(3),
        UNKNOWN(-1);

        private int value;

        private OsType(int value) {
            this.value = value;
        }

        public int getValue() {
            return this.value;
        }

        public String getStringValue() {
            return String.valueOf(this.value);
        }

        public static OsType valueOf(int value) {
            switch(value) {
                case 0:
                    return ANDROID;
                case 1:
                    return IPHONE;
                case 2:
                    return WP8;
                case 3:
                    return PC;
                default:
                    return UNKNOWN;
            }
        }

        public boolean equals(OsType type) {
            return type != null && this.value == type.value;
        }

        public boolean equals(int value) {
            return this.value == value;
        }

        public String toString() {
            return String.valueOf(this.value);
        }
    }
}


